#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHBoxLayout>
#include <QApplication>
#include <QFileInfo>
#include <QFileDialog>
#include <QProcess>
#include <QDebug>
#include <QMessageBox>
#include <QFile>
#include <qmath.h>
#include <QVector>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_magnifier.h>
#include <qwt_plot_panner.h>
#include <qwt_legend.h>
#include <qwt_plot_grid.h>
#include "qwt_point_data.h"
#include <qwt_plot_zoomer.h>
#include "sound.h"
#include "stft.h"
#include "formant.h"
#include "lpcc.h"
#include "mfcc.h"
#include "pitch.h"

#ifdef _WIN32
#include <windows.h>

std::string GbkToUtf8(const char *src_str)
{
    int len = MultiByteToWideChar(CP_ACP, 0, src_str, -1, NULL, 0);
    wchar_t* wstr = new wchar_t[len + 1];
    memset(wstr, 0, len + 1);
    MultiByteToWideChar(CP_ACP, 0, src_str, -1, wstr, len);
    len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
    char* str = new char[len + 1];
    memset(str, 0, len + 1);
    WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
    std::string strTemp = str;
    if (wstr) delete[] wstr;
    if (str) delete[] str;
    return strTemp;
}

std::string Utf8ToGbk(const char *src_str)
{
    int len = MultiByteToWideChar(CP_UTF8, 0, src_str, -1, NULL, 0);
    wchar_t* wszGBK = new wchar_t[len + 1];
    memset(wszGBK, 0, len * 2 + 2);
    MultiByteToWideChar(CP_UTF8, 0, src_str, -1, wszGBK, len);
    len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
    char* szGBK = new char[len + 1];
    memset(szGBK, 0, len + 1);
    WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
    std::string strTemp(szGBK);
    if (wszGBK) delete[] wszGBK;
    if (szGBK) delete[] szGBK;
    return strTemp;
}
#else
#include <iconv.h>

int GbkToUtf8(char *str_str, size_t src_len, char *dst_str, size_t dst_len)
{
    iconv_t cd;
    char **pin = &str_str;
    char **pout = &dst_str;

    cd = iconv_open("utf8", "gbk");
    if (cd == 0)
        return -1;
    memset(dst_str, 0, dst_len);
    if (iconv(cd, pin, &src_len, pout, &dst_len) == -1)
        return -1;
    iconv_close(cd);
    *pout = '\0';

    return 0;
}

int Utf8ToGbk(char *src_str, size_t src_len, char *dst_str, size_t dst_len)
{
    iconv_t cd;
    char **pin = &src_str;
    char **pout = &dst_str;

    cd = iconv_open("gbk", "utf8");
    if (cd == 0)
        return -1;
    memset(dst_str, 0, dst_len);
    if (iconv(cd, pin, &src_len, pout, &dst_len) == -1)
        return -1;
    iconv_close(cd);
    *pout = '\0';

    return 0;
}


#endif




MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //隐藏标题栏
//    setWindowFlags(Qt::SplashScreen);
//    ui->mainToolBar->hide();
//    ui->pushButton_1->setIcon(QIcon("C:\\Users\\YanSongWang\\Desktop\\III.jpg"));
//    ui->pushButton_2->setIcon(QIcon("C:\\Users\\YanSongWang\\Desktop\\ooo.png"));
//    ui->pushButton_3->setIcon(QIcon("C:\\Users\\YanSongWang\\Desktop\\XXX.jpg"));
//    setStyleSheet("QPushButton{border:none;}");
//    ui->groupBox_9->setStyleSheet("QGroupBox{border:none}QGroupBox{background-color:#212121;}");

    ui->lineEdit_F0_dt-> setPlaceholderText("10");
    ui->lineEdit_F1_dt-> setPlaceholderText("10");
    ui->lineEdit_LPC_dt->setPlaceholderText("10");
    ui->lineEdit_LPCC_dt->setPlaceholderText("10");
    ui->lineEdit_MFCC_dt->setPlaceholderText("10");
    ui->lineEdit_STFT_dt->setPlaceholderText("10");
    ui->lineEdit_F1_nF->setPlaceholderText("3");
    ui->lineEdit_F1_maxF->setPlaceholderText("5500");
    ui->lineEdit_F1_wlen->setPlaceholderText("50");
    ui->lineEdit_F0_lowF0->setPlaceholderText("60");
    ui->lineEdit_F0_highF0->setPlaceholderText("220");
    ui->lineEdit_STFT_wlen->setPlaceholderText("50");
    ui->lineEdit_LPC_p->setPlaceholderText("12");
    ui->lineEdit_LPC_wlen->setPlaceholderText("50");
    ui->lineEdit_LPCC_num->setPlaceholderText("12");
    ui->lineEdit_LPCC_lpcP->setPlaceholderText("12");
    ui->lineEdit_LPCC_wlen->setPlaceholderText("50");
    ui->lineEdit_MFCC_num->setPlaceholderText("12");
    ui->lineEdit_MFCC_filterNum->setPlaceholderText("40");
    ui->lineEdit_MFCC_lowF->setPlaceholderText("50");
    ui->lineEdit_MFCC_highF->setPlaceholderText("5500");
    ui->lineEdit_MFCC_wlen->setPlaceholderText("50");
}

//void MainWindow::on_pushButton_1_clicked()
//{
//    setWindowState(Qt::WindowMinimized);
//}

//void MainWindow::on_pushButton_2_clicked()
//{
//    setWindowState(Qt::WindowMaximized);
//}

//void MainWindow::on_pushButton_3_clicked()
//{
//    exit(0);
//}

MainWindow::~MainWindow()
{
    delete ui;
    delete winstft;
    delete winformant;
    delete winpitch;
    delete winlpc;
    delete winlpcc;
    delete winmfcc;
    data.Free();
}

static bool isFileExist(QString fullFilePath)
{
    QFileInfo fileInfo(fullFilePath);
    if(fileInfo.exists())
    {
        return true;
    }
    return false;
}

///######################################################导入音频\绘制波形图

void MainWindow::on_toolButton_filepath_clicked()
{
    QString s = QFileDialog::getOpenFileName(
                    this, "选择音频文件",
                    "/");
    if(!isFileExist(s)) return;
    ui->lineEdit_filepath->setText(s);
}

void MainWindow::on_pushButton_filepath_clicked()
{
    QString qpath = ui->lineEdit_filepath->text();
    qDebug() << qpath << "\n";
    if(qpath.isEmpty()) return;

    std::string path = qpath.toStdString();
    path = Utf8ToGbk(path.c_str());
    data = sound(path);
    int Len = data.getlength();
    double MNy, MXy, MNx, MXx;
    MNx = MNy = MXx = MXy = data[0];
    for(int i = 0; i < Len; ++ i) {
        MXy = max(MXy, data[i]);
        MNy = min(MNy, data[i]);
    }

    ui->qwtPlot_voiceWave->detachItems();
    ui->qwtPlot_voiceWave->replot();

    ui->qwtPlot_voiceWave->setAxisScale( QwtPlot::yLeft, MNy, MXy );
    ui->qwtPlot_voiceWave->setAxisScale( QwtPlot::xBottom, 0.0, 1.0*Len/data.getfrequency() );
    //qDebug()<<width<<height;
    //移动QwtPlot控件到父窗口的0，0起始位
//    ui->qwtPlot_voiceWave->move(0,0);
    //设置QwtPlot控件的大小
//    ui->qwtPlot_voiceWave->resize(width,height);
    //参数是QString
    ui->qwtPlot_voiceWave->setTitle(QObject::trUtf8("Wave"));
    ui->qwtPlot_voiceWave->setAxisTitle(QwtPlot::yLeft,QObject::trUtf8("Strength"));
    ui->qwtPlot_voiceWave->setAxisTitle(QwtPlot::yRight,QObject::trUtf8(""));
    ui->qwtPlot_voiceWave->setAxisTitle(QwtPlot::xBottom,QObject::trUtf8("time/s"));
    ui->qwtPlot_voiceWave->setAxisTitle(QwtPlot::xTop,QObject::trUtf8(""));
    ui->qwtPlot_voiceWave->setCanvasBackground( Qt::white );
    //微软雅黑
    //黑体
    QFont f("宋体", 12);//Helvetica [Cronyx]
    ui->qwtPlot_voiceWave->setAxisFont(QwtPlot::xBottom,f);
    //使用滚轮放大/缩小
    (void) new QwtPlotMagnifier(ui->qwtPlot_voiceWave->canvas() );

    //使用鼠标左键平移
//    (void) new QwtPlotPanner(ui->qwtPlot_voiceWave->canvas());

    //数据x,y值保存
     QVector<QPointF> vector;

     for(int i =0;i<Len;i++){
         QPointF point;
         point.setX(data.Sampled_indexToX(i));
         point.setY(data[i]);
         vector.append(point);
     }
     //构造曲线数据
     QwtPointSeriesData* series = new QwtPointSeriesData(vector);

     //create plot item
     QwtPlotCurve* curve1 = new QwtPlotCurve("Curve 1");
     curve1->setPen( Qt::blue, 1 );
     //设置数据
     curve1->setData(series);
     //把曲线附加到qwtPlot上
     curve1->attach(ui->qwtPlot_voiceWave);

     //加入网格
     QwtPlotGrid* grid = new QwtPlotGrid();
     grid->setPen(QColor(222,222,222),1);
     grid->attach(ui->qwtPlot_voiceWave);

     ui->qwtPlot_voiceWave->replot();
     ui->qwtPlot_voiceWave->show();

     QwtPlotZoomer *zoomer = new QwtPlotZoomer( ui->qwtPlot_voiceWave->canvas() );

     zoomer->setZoomBase(true);

     // 中键   Undo操作
     zoomer->setMousePattern(QwtEventPattern::MouseSelect3, Qt::MidButton );
     // 中键 + Ctrl Redo操作
     zoomer->setMousePattern(QwtEventPattern::MouseSelect6, Qt::MidButton, Qt::ControlModifier );
     // 中键 + Shift zoom base,即Home操作
     zoomer->setMousePattern(QwtEventPattern::MouseSelect2, Qt::MidButton, Qt::ShiftModifier );
     zoomer->setEnabled(true);
}

///######################################################绘制语谱图
void MainWindow::on_pushButton_STFT_clicked()
{
    if(data.getlength() == 0) {
        return;
    }
    QString qdt = ui->lineEdit_STFT_dt->text();
    qDebug() << qdt << "<<qdt \n";
    double dt, wlen;
    dt = qdt.isEmpty() ? 0.01 : qdt.toDouble() / 1000.0;
    QString qwlen = ui->lineEdit_STFT_wlen->text();
    wlen = qwlen.isEmpty() ? 0.05 : qwlen.toDouble() / 1000.0;
    QString WINFUN = ui->comboBox_STFT_winFun->currentText();
    vector<vector<double> > SF;

    qDebug() << "STFT\n";

    if(WINFUN == "RECTANGULAR") STFT(data,dt,wlen,Function_RECTANGULAR,SF);
    else if(WINFUN == "TRIANGULAR")  STFT(data,dt,wlen,Function_TRIANGULAR,SF);
    else if(WINFUN == "HANNING") STFT(data,dt,wlen,Function_HANNING,SF);
    else if(WINFUN == "HAMMING") STFT(data,dt,wlen,Function_HAMMING,SF);

    qDebug() << SF.size() <<" " << SF[0].size() << " STFT END\n";

//    ofstream fout("D:\\rl001.txt");
//    for(int i = 0; i < SF.size(); ++ i) {
//        for(int j = 0 ; j < SF[i].size(); ++ j)
//            fout << SF[i][j] << ","; fout << endl;
//    }fout.close();

    for(int i = 0; i < SF.size(); ++ i) {
        for(int j = 0 ; j < SF[i].size(); ++ j)
            if(abs(SF[i][j]) > 1000) SF[i][j] = 0;
    }


    winstft = new WindowSTFT(SF, 1.0*data.getlength()/data.getfrequency(), 0.0, data.getfrequency()/2.0, this);

    qDebug() << "WINDOW STFT END\n";
    winstft->setWindowTitle(QString("STFT"));
    QPalette pal; pal.setBrush(QPalette::Background,Qt::white);
    winstft->setPalette(pal);
    winstft->show();

    for(auto & V: SF) vector<double>().swap(V);
    vector<vector<double> >().swap(SF);
}

///######################################################绘制共振峰散点图
void MainWindow::on_pushButton_F1_clicked()
{
    if(data.getlength() == 0) {
        return;
    }
    double dt, wlen, maxF;
    int nF;
    QString qdt = ui->lineEdit_F1_dt->text();
    dt = qdt.isEmpty() ? 0.01 : qdt.toDouble() / 1000.0;
    QString qwlen = ui->lineEdit_F1_wlen->text();
    wlen = qwlen.isEmpty() ? 0.05 : qwlen.toDouble() / 1000.0;    QString qnF = ui->lineEdit_F1_nF->text();
    nF = qnF.isEmpty() ? 3 : qnF.toInt();
    QString qmaxF = ui->lineEdit_F1_maxF->text();
    maxF = qmaxF.isEmpty() ? 5500 : qmaxF.toDouble();
//    QString qenhan = ui->lineEdit_F1_enhan->text();
//    double enhan = qenhan.toDouble();
    Formant FF = Sound_to_Formant_burg(data, dt, nF, maxF, wlen / 2, 50);
    winformant = new WindowFormant(FF,this);
    winformant->setWindowTitle(QString("Formant"));
    QPalette pal; pal.setBrush(QPalette::Background,Qt::white);
    winformant->setPalette(pal);
    winformant->show();

    FF.Free();
}

///######################################################绘制基频散点图

void MainWindow::on_pushButton_F0_clicked()
{
    if(data.getlength() == 0) {
        return;
    }
    double dt, wlen, highF0, lowF0;
    QString qdt = ui->lineEdit_F0_dt->text();
    dt = qdt.isEmpty() ? 0.01 : qdt.toDouble() / 1000.0;
    QString qhighF0 = ui->lineEdit_F0_highF0->text();
    highF0 = qhighF0.isEmpty() ? 220 : qhighF0.toDouble();
    QString qlowF0 = ui->lineEdit_F0_lowF0->text();
    lowF0 = qlowF0.isEmpty() ? 60 : qlowF0.toDouble();
    Pitch F0 = Sound_to_Pitch(data, dt, lowF0, highF0);
    winpitch = new WindowPitch(F0,this);
    winpitch->setWindowTitle(QString("Pitch"));
    QPalette pal; pal.setBrush(QPalette::Background,Qt::white);
    winpitch->setPalette(pal);
    winpitch->show();
    F0.Free();
}

///######################################################导出LPC TEXT

void MainWindow::on_pushButton_LPC_clicked()
{
    if(data.getlength() == 0) {
        return;
    }
    double dt, wlen;
    int p;
    QString qdt = ui->lineEdit_LPC_dt->text();
    dt = qdt.isEmpty() ? 0.01 : qdt.toDouble() / 1000.0;
    QString qwlen = ui->lineEdit_LPC_wlen->text();
    wlen = qwlen.isEmpty() ? 0.05 : qwlen.toDouble() / 1000.0;
    QString qp = ui->lineEdit_LPC_p->text();
    p = qp.isEmpty() ? 12 : qp.toInt();

    vector<vector<double> > dataF = data.enframe((int)(wlen * data.getfrequency()),(int)(dt * data.getfrequency()), Function_RECTANGULAR);
    vector<vector<double> > ar(dataF.size());
    vector<double> ti;

    double t1 = wlen / 2;
    for(uint i = 0; i < dataF.size(); ++ i)
        lpc(dataF[i], ar[i], p), ti.push_back(t1 + i * dt), ar[i].pop_back();

    winlpc = new WindowLPC(ti, ar, this);
    winlpc->setWindowTitle(QString("LPC"));
    QPalette pal; pal.setBrush(QPalette::Background,Qt::white);
    winlpc->setPalette(pal);
    winlpc->show();

    vector<double>().swap(ti);
    vector<vector<double>>().swap(ar);
    vector<vector<double>>().swap(dataF);
}

///######################################################导出LPCC TEXT
inline v_d operator - (const v_d A, const v_d B) {
    assert(A.size() == B.size());
    v_d C(A.size());
    for(int i = 0; i < A.size(); ++ i) {
        C[i] = A[i] - B[i];
    }
    return C;
}
inline v_d operator + (const v_d A, const v_d B) {
    assert(A.size() == B.size());
    v_d C(A.size());
    for(int i = 0; i < A.size(); ++ i) {
        C[i] = A[i] + B[i];
    }
    return C;
}
inline v_d operator / (const v_d A, const double B) {
    assert(B > 0);
    v_d C(A.size());
    for(int i = 0; i < A.size(); ++ i) {
        C[i] = A[i] / B;
    }
    return C;
}
std::vector<v_d> diff(const std::vector<v_d> &A, int K){
    std::vector<v_d> d(A.size(),v_d(A[0].size(), 0));
    for(int t = 0; t < A.size(); ++ t) {
        if(t < K)
            d[t] = A[t+1] - A[t];
        else if(t >= A.size()-K)
            d[t] = A[t] - A[t-1];
        else {
            int sum = 0;
            for(int k = 1; k <= K; ++ k) {
                d[t] = d[t] + (A[t+k] - A[t-k]) / (1.0/k);
                sum += k * k;
            }
            d[t] = d[t] / sqrt(2.0 * sum);
        }
    }
    return d;
}
void MainWindow::on_pushButton_LPCC_clicked()
{
    if(data.getlength() == 0) {
        return;
    }
    double dt,wlen;
    int lpcP, num;
    QString qdt = ui->lineEdit_LPCC_dt->text();
    dt = qdt.isEmpty() ? 0.01 : qdt.toDouble() / 1000.0;
    QString qwlen = ui->lineEdit_LPCC_wlen->text();
    wlen = qwlen.isEmpty() ? 0.05 : qwlen.toDouble() / 1000.0;
    QString qlpcP = ui->lineEdit_LPCC_lpcP->text();
    lpcP = qlpcP.isEmpty() ? 12 : qlpcP.toInt();
    QString qnum = ui->lineEdit_LPCC_num->text();
    num = qnum.isEmpty() ? 12 : qnum.toInt();
    vector<vector<double> > dataF = data.enframe((int)(wlen * data.getfrequency()),(int)(dt * data.getfrequency()), Function_RECTANGULAR);
    vector<vector<double> > lpccFp(dataF.size()), lpcc_dFp(dataF.size()), lpcc_ddFp(dataF.size());
    vector<double> ti;

    qDebug() << "LPCC start\n";
    double t1 = wlen / 2;
    for(uint i = 0; i < dataF.size(); ++ i)
        lpcc(dataF[i],lpccFp[i], lpcP, num), ti.push_back(t1 + i * dt), lpccFp[i].erase(lpccFp[i].begin());
    qDebug() << "LPCC end\n";
    lpcc_dFp = diff(lpccFp, 2);
    lpcc_ddFp = diff(lpcc_dFp, 2);

    winlpcc = new WindowLPCC(ti, lpccFp, lpcc_dFp, lpcc_ddFp, this);
    winlpcc->setWindowTitle(QString("LPCC"));
    QPalette pal; pal.setBrush(QPalette::Background,Qt::white);
    winlpcc->setPalette(pal);
    winlpcc->show();

    vector<double>().swap(ti);
    vector<vector<double>>().swap(lpccFp);
    vector<vector<double>>().swap(lpcc_dFp);
    vector<vector<double>>().swap(lpcc_ddFp);
    vector<vector<double>>().swap(dataF);
}

///######################################################导出MFCC TEXT
void MainWindow::on_pushButton_MFCC_clicked()
{
    if(data.getlength() == 0) {
        return;
    }
    QString qdt = ui->lineEdit_MFCC_dt->text();
    double dt = qdt.isEmpty() ? 10.0 : qdt.toDouble();
    QString qwlen = ui->lineEdit_MFCC_wlen->text();
    double wlen = qwlen.isEmpty() ? 50.0 : qwlen.toDouble();
    QString qfilterNum = ui->lineEdit_MFCC_filterNum->text();
    int filterNum = qfilterNum.isEmpty() ? 40 : qfilterNum.toInt();
    QString qnum = ui->lineEdit_MFCC_num->text();
    int num = qnum.isEmpty() ? 12 : qnum.toInt();
    QString qlowF = ui->lineEdit_MFCC_lowF->text();
    double lowF = qlowF.isEmpty() ? 50 : qlowF.toDouble();
    QString qhighF = ui->lineEdit_MFCC_highF->text();
    double highF = qhighF.isEmpty() ? 5500 : qhighF.toDouble();


    MFCC mfcc_cal(data.getfrequency(),num, wlen, dt, filterNum,lowF, highF,MFCC_INIT_TYPE_MILLISECONDS);

    AudioFile<double> AF;
    vector<int> mfcc_sound(data.getlength());
    for(int i = 0; i < data.getlength(); ++ i) {
        mfcc_sound[i] = AF.sampleToSixteenBitInt(data[i]);
    }
    vector<double> ti;
    vector<v_d> mfccFp, mfcc_dFp, mfcc_ddFp;
    mfcc_cal.process_v2v(mfcc_sound, mfccFp);
    mfcc_dFp.resize(mfccFp.size());
    mfcc_ddFp.resize(mfccFp.size());
    double t1 = wlen/1000.0 / 2;
    for(size_t i= 0;i < mfccFp.size(); ++ i) {
        mfccFp[i].pop_back();ti.push_back(t1 + dt / 1000.0 * i);
    }
    mfcc_dFp = diff(mfccFp, 2);
    mfcc_ddFp = diff(mfcc_dFp, 2);

//    for(auto & aa: mfccFp)for(auto & bb: aa) bb = 20 * log10(bb);
//    winstft = new WindowSTFT(mfccFp, 1.0*data.getlength()/data.getfrequency(), 0,mfccFp.size(), this);
//    winstft->show();

    winmfcc = new WindowMFCC(ti, mfccFp, mfcc_dFp, mfcc_ddFp, this);
    winmfcc->setWindowTitle(QString("MFCC"));
    QPalette pal; pal.setBrush(QPalette::Background,Qt::white);
    winmfcc->setPalette(pal);
    winmfcc->show();

    vector<double>().swap(ti);
    vector<vector<double>>().swap(mfccFp);
    vector<vector<double>>().swap(mfcc_dFp);
    vector<vector<double>>().swap(mfcc_ddFp);
    vector<int>().swap(mfcc_sound);
}
